//
//  DemoFaceLiveNetwork.swift
//  GLUZSwift
//
//  Created by xyanl on 2025/7/8.
//

import Foundation
import CommonCrypto

class DemoFaceLiveNetwork {
    
    static let shared = DemoFaceLiveNetwork()
    
    private let networkHost = "https://api.yljz.com"
    private let networkTimeout = 30
    
    private let apiKey = "hkQtUrIRRCrzhOAqZj2u4nz7Ip5-KdFg"
    private let apiSecret = "GJoNjbtwYxLYnJPRtV_4ZxrNhbymHwAM"
    
    private var formMPboundary: String = "4123h4l1k24hk123jh4jkhfksdhafkljh23kj41h23kj4h1kh4k23jh"
    private var startMPboundary: String {
        return "--\(formMPboundary)"
    }
    private var endMPboundary: String {
        return "\(startMPboundary)--"
    }
    
    typealias RequestSuccess = (Int, [String: Any]) -> Void
    typealias RequestFailure = (Int, Error) -> Void
    
    // MARK: - Header API
    
    func getV5BizTokenWith(livenessID: String, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let params: [String: Any] = [
            "sign": getFinAuthSignStr(),
            "sign_version": getFinAuthSignVersionStr(),
            "liveness_id": livenessID
        ]
        
        let urlStr = "\(networkHost)/finauth/v5/sdk/get_biz_token"
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        requestAppendParameter(request: &request, parameters: params)
        requestAppendEND(request: &request)
        requestAppendHeaderField(request: &request)
        
        sendAsynchronousRequest(request: request, success: success, failure: failure)
    }
    
    func liveness(token: String, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let params: [String: Any] = [
            "sign": getFinAuthSignStr(),
            "sign_version": getFinAuthSignVersionStr(),
            "data_type": 0,
            "biz_token": token,
            "encryption_type": 0
        ]
        
        let urlStr = "\(networkHost)/finauth/v5/sdk/liveness"
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        requestAppendParameter(request: &request, parameters: params)
        requestAppendEND(request: &request)
        requestAppendHeaderField(request: &request)
        
        sendAsynchronousRequest(request: request, success: success, failure: failure)
    }
    
    
    
    
    
    
    func getV5VerifyWith(bizToken: String, verifyID: String, idcardName: String, idcardNumber: String, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let params: [String: Any] = [
            "sign": getFinAuthSignStr(),
            "sign_version": getFinAuthSignVersionStr(),
            "data_type": "0",
            "biz_token": bizToken,
            "encryption_type": "0",
            "verify_id": verifyID,
            "comparison_type": "0",
            "idcard_name": idcardName,
            "idcard_number": idcardNumber,
            "uuid": UUID().uuidString
        ]
        
        let urlStr = "\(networkHost)/finauth/v5/sdk/verify"
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        requestAppendParameter(request: &request, parameters: params)
        requestAppendEND(request: &request)
        requestAppendHeaderField(request: &request)
        
        sendAsynchronousRequest(request: request, success: success, failure: failure)
    }
    
    func getV5VerifyWith(bizToken: String, verifyID: String, faceImage: UIImage, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let params: [String: Any] = [
            "sign": getFinAuthSignStr(),
            "sign_version": getFinAuthSignVersionStr(),
            "data_type": "0",
            "biz_token": bizToken,
            "encryption_type": "0",
            "verify_id": verifyID,
            "comparison_type": "0",
            "uuid": UUID().uuidString
        ]
        
        let urlStr = "\(networkHost)/finauth/v5/sdk/verify"
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        requestAppendParameter(request: &request, parameters: params)
        if let imageData = faceImage.pngData() {
            requestAppendFile(request: &request, fileData: imageData, fileName: "image_ref1", contentType: "image/png")
        }
        requestAppendEND(request: &request)
        requestAppendHeaderField(request: &request)
        
        sendAsynchronousRequest(request: request, success: success, failure: failure)
    }
    
    func getV5LicenseAndConfigWith(bodyInfo: [String: Any], success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        // TODO: Need to set config API HostURL
        let urlStr = ""
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        request.addValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: bodyInfo, options: [])
        
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: request) { data, response, error in
            if let error = error {
                failure((response as? HTTPURLResponse)?.statusCode ?? 0, error)
                return
            }
            
            guard let data = data,
                  let responseDict = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String: Any] else {
                failure((response as? HTTPURLResponse)?.statusCode ?? 0, NSError(domain: "", code: -1, userInfo: nil))
                return
            }
            
            success((response as? HTTPURLResponse)?.statusCode ?? 0, responseDict)
        }
        task.resume()
    }
    
    func getV5VerifyWith(livenessType: String, delta: Data, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let params: [String: Any] = [
            "liveness_type": livenessType,
            "mode": "1"
        ]
        
        let urlStr = ""
        var request = getRequest(url: URL(string: urlStr)!, method: "POST")
        
        requestAppendParameter(request: &request, parameters: params)
        requestAppendFile(request: &request, fileData: delta, fileName: "facelive_data", contentType: "text/html")
        requestAppendEND(request: &request)
        requestAppendHeaderField(request: &request)
        
        sendAsynchronousRequest(request: request, success: success, failure: failure)
    }
    
    // MARK: - Network
    
    private func getRequest(url: URL, method: String) -> URLRequest {
        var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: TimeInterval(networkTimeout))
        request.httpMethod = method
        return request
    }
    
    private func requestAppendParameter(request: inout URLRequest, parameters: [String: Any]) {
        var tempString = ""
        
        for (key, value) in parameters {
            tempString += "\(startMPboundary)\r\n"
            tempString += "Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n"
            tempString += "\(value)\r\n"
        }
        
        if let dicData = tempString.data(using: .utf8) {
            requestUpdata(request: &request, data: dicData)
        }
    }
    
    private func requestAppendFile(request: inout URLRequest, fileData: Data, fileName: String, contentType: String) {
        var tempString = ""
        tempString += "\(startMPboundary)\r\n"
        tempString += "Content-Disposition: form-data; name=\"\(fileName)\"; filename=\"\(fileName)\"\r\n"
        tempString += "Content-Type: \(contentType)\r\n\r\n"
        
        if let tempData = tempString.data(using: .utf8) {
            var resultData = tempData
            resultData.append(fileData)
            if let tempData2 = "\r\n".data(using: .utf8) {
                resultData.append(tempData2)
            }
            requestUpdata(request: &request, data: resultData)
        }
    }
    
    private func requestAppendHeaderField(request: inout URLRequest) {
        let content = "multipart/form-data; boundary=\(formMPboundary)"
        request.setValue(content, forHTTPHeaderField: "Content-Type")
    }
    
    private func requestAppendEND(request: inout URLRequest) {
        if let endData = endMPboundary.data(using: .utf8) {
            requestUpdata(request: &request, data: endData)
        }
    }
    
    private func requestUpdata(request: inout URLRequest, data: Data) {
        if let body = request.httpBody {
            var resultData = body
            resultData.append(data)
            request.httpBody = resultData
        } else {
            request.httpBody = data
        }
    }
    
    private func sendAsynchronousRequest(request: URLRequest, success: @escaping RequestSuccess, failure: @escaping RequestFailure) {
        let session = URLSession(configuration: .default)
        let task = session.dataTask(with: request) { data, response, error in
            DispatchQueue.main.async {
                if let error = error {
                    failure((response as? HTTPURLResponse)?.statusCode ?? 0, error)
                    return
                }
                
                guard let data = data,
                      let responseDict = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String: Any] else {
                    failure((response as? HTTPURLResponse)?.statusCode ?? 0, NSError(domain: "", code: -1, userInfo: nil))
                    return
                }
                
                success((response as? HTTPURLResponse)?.statusCode ?? 0, responseDict)
            }
        }
        task.resume()
    }
    
    // MARK: - Sign
    
    private func getFinAuthSignStr() -> String {
        let validDuration = 1000
        let currentTime = Int(Date().timeIntervalSince1970)
        let expireTime = currentTime + validDuration
        let random = abs(Int.random(in: 0..<100000000000))
        
        let str = "a=\(apiKey)&b=\(expireTime)&c=\(currentTime)&d=\(random)"
        let cKey = apiSecret.cString(using: .utf8)
        let cData = str.cString(using: .utf8)
        var cHMAC = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
        
        if let cKey = cKey, let cData = cData {
            CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, cKey.count - 1, cData, cData.count - 1, &cHMAC)
        }
        
        let HMAC = Data(bytes: cHMAC, count: cHMAC.count)
        let signRawData = str.data(using: .utf8)!
        var data = HMAC
        data.append(signRawData)
        
        return data.base64EncodedString()
    }
    
    private func getFinAuthSignVersionStr() -> String {
        return "hmac_sha1"
    }
}
